#include "../base/conf.h"
#include "../base/th.h"
/* protocol header file */
#include "../server/msg.h"
#include "../server/slave_protocol.h"
#include "slave_timer_protocol.h"
#include "slave_logger_protocol.h"
#include "slave_timer_reqlist.h"



#include <time.h>
#include <stdio.h>
#include <assert.h>

C_CODE_BEGIN
/*
 * This structure is for internally use not for export protocol 
 */
struct timer_req {
	/* Once the number of ticks time has been elapsed, the slave will receive a message */
	size_t ticks;
	const char* slavename;
};


/*
 * Timer context for timer slave
 * Including a thread handler to control the back thread for timer and also
 * a queue for storing the notification requests.
 */

struct timer_context {
	/* queue for timer */
	struct slave_timer_reqlist* reqlist;
	/* thread handler for back thread */
	thread_handler backth;
	/* slave api , this should be stored here since the sending message operation
	 * will be done by another thread */
	struct slave_api* api;
	/*
	 * command for back end thread. This area is typically in use when the
	 * user specifying unload this timer slave , and the clean function will
	 * notify the back thread to exit and join this thread as well
	 */
	const char thread_cmd[12];
	/*
	 * Resolution of the timer 
	 */
	size_t res;
};

static const char* _slave_timer="Timer";


/* 
 * Send notification 
 */

static void _send_msg( struct timer_context* ctx, const char* tsl ) {
	struct message msg;
	/* just a dummy message to notify that slave */
	msg.clean = NULL;
	msg.msg = NULL;
	msg.sz = 0;
	/* send message */
	ctx->api->sendmessage(&msg,tsl,ctx->api);
}

static void* _timer_thread_cb( void* arg ) {
	struct timer_context *ctx = (struct timer_context*)(arg);
	clock_t prev = clock();
	clock_t cur ;
	clock_t elapsed;
	struct th_join_sec sleep_time;
	void* ite_b,*ite_e;
	struct slave_timer_req_runtime* rt;

	while( 1 ) {
		/* check the cmd for this thread */
		if( strcmp(ctx->thread_cmd,"EXIT") ==0 )
			break;
		cur = clock();
		/* get the elapsed ticks */
		elapsed = cur-prev;
		prev=cur;
		/* handle the message dispatching operation here 
		 * dispatch clock tick message to the slave which
		 * has requested message notification */
		ite_b = _reqlist_begin_iterator(ctx->reqlist);
		ite_e = _reqlist_end_iterator(ctx->reqlist);
		while( ite_b != ite_e ) {
			rt = _reqlist_deref_iterator(ite_b);
			assert(rt);
			rt->elapsed_tick += elapsed;
			if( rt->elapsed_tick >= rt->req.ticks ) {
				if( rt->req.ttf > 0 ) {
					--(rt->req.ttf);
					_send_msg(ctx,rt->name);
				}
			}
			ite_b=_reqlist_move_iterator(ctx->reqlist,ite_b);
		}
		sleep_time.sec = ctx->res;
		sleep_time.micro_sec = 0;
		/* sleep this thread */
		thread_sleep(&sleep_time);
	}
	return NULL;
}	

static int _timer_slave_init(  void* ctx ) {
	struct timer_context* tctx = (struct timer_context*)(ctx);
	tctx->reqlist = _reqlist_create(20);
	tctx->res = 1;
	strcpy((char*)tctx->thread_cmd,"");
	/* initialize the timer thread */
	if( !thread_create(&tctx->backth,"TimerThread",_timer_thread_cb,tctx,false) ) {
		_reqlist_destroy(tctx->reqlist);
		return -1;
	}
	return 1;
}

static void _timer_slave_notification( struct slave_api* api , void* ctx ) {
	struct timer_context* tctx = (struct timer_context*)(ctx);
	tctx->api = api;
}

static void _log_error_clean( struct message* msg ) {
	free(msg->msg);
}

static void _log( struct slave_api* api , const char* level ,const char* src , size_t sid , const char* content , const char* others) {
	struct message errormsg;
	struct slave_logger_protocol* lprotocol;
	static const char* err = "Can not add this timer!\nThis timer must have been added before!\n";
	errormsg.msg = malloc( sizeof(struct slave_logger_protocol) + 512*sizeof(char) );
	lprotocol = (struct slave_logger_protocol*)(errormsg.msg);
	errormsg.src = _slave_timer;
	errormsg.clean = _log_error_clean;
	errormsg.sz = 512*sizeof(char);
	lprotocol->type = level;
	lprotocol->content=((char*)(errormsg.msg)+sizeof(struct slave_logger_protocol));
	sprintf((char*)lprotocol->content,"Source: %s.\nSession ID: %d.\n%s\nOthers:",src,sid,content);
	api->sendmessage(&errormsg,"Logger",api);
}


static void _timer_slave_cb( struct message* msg , void* arg , struct slave_api* api ) {
	/*
	 * This message is typically the request message which wraps the time notification
	 * requests.We know the slave name by the message itself.
	 */
	struct slave_timer_protocol* proto = (struct slave_timer_protocol*)(msg->src);
	struct timer_req tr;
	struct timer_context* tc = (struct timer_context*)(arg);
	struct slave_timer_req* timer_req;
	struct slave_timer_req_runtime rt;
	struct slave_timer_req* edit_new_ptr;
	struct slave_timer_req_runtime* rt_ptr;
	bool bret;
	tr.slavename = msg->src;
	/* add timer operation */
	if( strcmp(proto->op,slave_timer_add_timer_op) ==0 ) {
		/* add timer operation */
		timer_req= (struct slave_timer_req*)(proto->data);
		rt.req = *timer_req;
		rt.elapsed_tick= 0;
		strcpy( (char*)rt.name , msg->src );
		bret = _reqlist_insert( tc->reqlist ,  &rt );
		if(!bret) {
			/* add an error to log operation */
			_log(api,_slave_logger_error_level,msg->src,msg->sid,"Add timer failed.This timer has been added before!\n","");
		} else {
			/* log add timer operation has been finished */
			_log(api,_slave_logger_info_level,msg->src,msg->sid,"Add timer successfully!\n","");
		}
	} else if( strcmp(proto->op,slave_timer_remove_timer_op) == 0 ) {
		/* remove timer */
		bret = _reqlist_remove(tc->reqlist,msg->src);
		if(!bret) {
			_log(api,_slave_logger_error_level,msg->src,msg->sid,"Remove timer failed!","");
		} else {
			_log(api,_slave_logger_info_level,msg->src,msg->sid,"Remove timer successfully!","");
		}
	} else if( strcmp(proto->op,slave_timer_edit_timer_op) ==0 ) {
		/* edit timer */
		rt_ptr = _reqlist_query(tc->reqlist,msg->src);
		if( rt_ptr == NULL ) {
			_log(api,_slave_logger_error_level,msg->src,msg->sid,"Edit timer failed!\nTimer not existed!","");
		} else {
			edit_new_ptr = (struct slave_timer_req*)(proto->data);
			rt_ptr->req = *edit_new_ptr;
			_log(api,_slave_logger_info_level,msg->src,msg->sid,"Edit timer successfully!","");
		}
	} else if( strcmp(proto->op,slave_timer_resolution_op)==0 ) {
		/* setting the resolution atomically to ensure no partial copy existed */
#ifdef _MSC_VER
	InterlockedExchange((volatile size_t*)(&(tc->res)),(LONG)(*(size_t*)(proto->data)));
#elif __GNUC__
	__sync_lock_test_and_set(&(tc->res), *(size_t*)(proto->data) );
#else
#endif
		_log(api,_slave_logger_info_level,msg->src,msg->sid,"Timer Resolution changed!","");
	} else {
		/* unrecognized operation */
		_log(api,_slave_logger_warn_level,msg->src,msg->sid,"Unrecognized operation!",proto->op);
	}
}

static void _timer_slave_clean( void* arg ) {
	struct timer_context* tctx = (struct timer_context*)(arg);
	strcpy((char*)(tctx->thread_cmd),"EXIT");
	thread_join(tctx->backth,NULL,NULL);
	if( tctx->reqlist != NULL )
		_reqlist_destroy(tctx->reqlist);
	free(tctx);
}


EXPORT_FUNC int entry( struct slave_protocol* slpro ) {
	struct timer_context* tctx = malloc(sizeof(struct timer_context));
	slpro->argument = tctx;
	slpro->cb = _timer_slave_cb;
	slpro->init=_timer_slave_init;
	slpro->slavename = _slave_timer;
	slpro->clean=_timer_slave_clean;
	slpro->notify=_timer_slave_notification;
	tctx->reqlist = NULL;
	return 1;
}

C_CODE_END













